package com.zhou.designParten.demo1;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

/**
 * 每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ，其中序列的长度必须为 2n。
 * 示例 1：
 *
 * 输入：n = 2
 * 输出："0102"
 * 说明：三条线程异步执行，其中一个调用 zero()，另一个线程调用 even()，最后一个线程调用odd()。正确的输出为 "0102"。
 * 示例 2：
 *
 * 输入：n = 5
 * 输出："0102030405"
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/print-zero-even-odd
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
class ZeroEvenOdd {
    private int n;
    private volatile int index = 0;
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    private final ConcurrentHashMap<String,Thread> concurrentHashMap = new ConcurrentHashMap<>();

    public ZeroEvenOdd(int n) {
        this.n = n;
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        concurrentHashMap.put("zero",Thread.currentThread());
        for(int i=0;i<n;i++){
            while(index!=0){
                LockSupport.park();
            }
            printNumber.accept(0);
            atomicInteger.getAndIncrement();//自增1
            if(i%2==0){
                index =2;
            }else{
                index =1;
            }
            concurrentHashMap.forEach((key,value)->LockSupport.unpark(value));
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        concurrentHashMap.put("even",Thread.currentThread());
        for(int i =0;i<n/2;i++){
            while(index!=1){
                LockSupport.park();
            }
            printNumber.accept(atomicInteger.intValue());
            index = 0;
            LockSupport.unpark(concurrentHashMap.get("zero"));
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        concurrentHashMap.put("odd",Thread.currentThread());
        for(int i =0;i<(n+1)/2;i++){
            while(index!=2){
                LockSupport.park();
            }
            printNumber.accept(atomicInteger.intValue());
            index = 0;
            LockSupport.unpark(concurrentHashMap.get("zero"));
        }
    }

    public static void main(String[] args) {
        ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(8);
        new Thread(() -> {
            try {
                zeroEvenOdd.zero(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.even(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.odd(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

}

/**
 * 方式2：阻塞队列
 */
class ZeroEvenOdd2 {
    private int n;
    private BlockingQueue<Integer> zeroQueue = new LinkedBlockingQueue<>(1);
    private BlockingQueue<Integer> evenQueue = new LinkedBlockingQueue<>(1);
    private BlockingQueue<Integer> oddQueue = new LinkedBlockingQueue<>(1);
    private AtomicInteger atomicInteger = new AtomicInteger(0);


    public ZeroEvenOdd2(int n) throws InterruptedException {
        this.n = n;
        zeroQueue.put(1);//第一步控制
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=0;i<n;i++){
            zeroQueue.take();//消费，如果没有就阻塞住

            printNumber.accept(0);
            atomicInteger.getAndIncrement();//自增1
            if(i%2==0){//奇偶数字控制
                oddQueue.put(1);
            }else{
                evenQueue.put(0);
            }
          }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i =0;i<n/2;i++){
            evenQueue.take();
            printNumber.accept(atomicInteger.intValue());
            zeroQueue.put(1);
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i =0;i<(n+1)/2;i++){
            oddQueue.take();
            printNumber.accept(atomicInteger.intValue());
            zeroQueue.put(1);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ZeroEvenOdd2 zeroEvenOdd = new ZeroEvenOdd2(8);
        new Thread(() -> {
            try {
                zeroEvenOdd.zero(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.even(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.odd(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

/**
 * 方式2：阻塞队列
 */
class ZeroEvenOdd3 {
    private int n;
    private volatile int index =0;

    private AtomicInteger atomicInteger = new AtomicInteger(0);
    private final Object o = new Object();


    public ZeroEvenOdd3(int n){
        this.n = n;
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i =0;i<n;i++){
            synchronized (o){
                if(index!=0){
                  o.wait();
                }
                printNumber.accept(0);
                atomicInteger.getAndIncrement();//自增1
                if(i%2==0){//奇偶数字控制
                    index =1;
                }else{
                    index =2;
                }
                o.notifyAll();
            }

        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i =0;i<n/2;i++) {
            synchronized (o) {
                if (index != 1) {
                    o.wait();
                }
                printNumber.accept(atomicInteger.intValue());
                index = 0;
                o.notifyAll();
            }
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i =0;i<(n+1)/2;i++){
            synchronized (o){
                if(index!=2){
                    o.wait();
                }
                printNumber.accept(atomicInteger.intValue());
                index =0;
                o.notifyAll();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ZeroEvenOdd3 zeroEvenOdd = new ZeroEvenOdd3(8);
        new Thread(() -> {
            try {
                zeroEvenOdd.zero(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.even(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.odd(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}